package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

var he1 = []byte("HTTP/1.1 200 OK")
var he2 = []byte("\r\n")
var he3 = []byte("Content-Length: 300")
var he4 = []byte("\r\n")
var he5 = []byte("Content-Type: text/css; charset=utf-8")
var he6 = []byte("\r\n")
var he7 = []byte("\r\n")
var headers []byte
var buf300 = new(bytes.Buffer)

var gangr = []byte("\r")
var gangn = []byte("\n")
var kongge = []byte(" ")

var fav1 = []byte("HTTP/1.1 200 OK")
var fav2 = []byte("Content-Type: image/x-icon")
var fav3 = []byte("Content-Length: 4286")
var faviconHeaders []byte

func init() {
	headers = make([]byte, 0, 0)
	headers = append(headers, he1...)
	headers = append(headers, he2...)
	headers = append(headers, he3...)
	headers = append(headers, he4...)
	headers = append(headers, he5...)
	headers = append(headers, he6...)
	headers = append(headers, he7...)

	faviconHeaders = make([]byte, 0, 0)
	faviconHeaders = append(faviconHeaders, fav1...)
	faviconHeaders = append(faviconHeaders, he2...)
	faviconHeaders = append(faviconHeaders, fav2...)
	faviconHeaders = append(faviconHeaders, he2...)
	faviconHeaders = append(faviconHeaders, fav3...)
	faviconHeaders = append(faviconHeaders, he2...)
	faviconHeaders = append(faviconHeaders, he2...)
	file, _ := os.Open("/dist/favicon.ico")
	faviconBytes, _ := ioutil.ReadAll(file)
	faviconHeaders = append(faviconHeaders, faviconBytes...)
	file.Close()
}

func gen300() []byte {
	he8 := make([]byte, 300, 300)
	for idx := range he8 {
		he8[idx] = 'a' + byte(time.Now().Nanosecond()%22)
	}
	return he8
}

func forward(lport string, thost string, tport string) {
	hostPort := "0.0.0.0:" + lport
	l, e := net.Listen("tcp4", hostPort)
	if e != nil {
		log.Println(e.Error())
		os.Exit(0)
	}
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Println(err.Error())
			os.Exit(0)
		}
		go handleForward(conn, thost, tport)
	}
}

func handleForward(conn net.Conn, thost string, tport string) {
	log.Println("+++++++++++++++++++++++++++++++++++++")
	defer log.Println("----------------------------------")
	defer conn.Close()

	//base setting
	timeout, _ := time.ParseDuration("100ms")
	conn.SetReadDeadline(time.Now().Add(timeout))

	b := make([]byte, 22048, 22048)
	// three time reading
	log.Println("ACC", 1)
	n1, _ := conn.Read(b)
	log.Println("ACC", 2)
	n2, _ := conn.Read(b[n1:])
	log.Println("ACC", 3)
	n3, _ := conn.Read(b[n1+n2:])
	log.Println("ACC", 4)
	n4, _ := conn.Read(b[n1+n2+n3:])
	rall := n1 + n2 + n3 + n4
	log.Println(rall, n1, n2, n3, n4)
	if rall == 0 {
		log.Println("rall-->0")
		return
	}
	recvMsg := strings.Replace(string(b[:rall]), "\r", "", -1)
	log.Println(len(recvMsg))
	recvMsg = strings.Replace(recvMsg, "\t", "", -1)
	log.Println(len(recvMsg))
	log.Println(recvMsg)
	if strings.Index(recvMsg, "favicon") >= 0 {
		log.Println("<><><>favicon recved<><><>")
		conn.Write(faviconHeaders)
		return
	}
	var connToHosta net.Conn
	for {
		connToHost, err := net.Dial("tcp4", thost+":"+tport)
		if err != nil {
			log.Println(err.Error())
			os.Exit(0)
		}
		timeout, _ = time.ParseDuration("3s")

		connToHost.SetReadDeadline(time.Now().Add(timeout))

		defer connToHost.Close()
		log.Println("Write to inside: hoping ", rall)
		wti, _ := connToHost.Write(b[:rall])
		log.Println("Write to inside: reality ", wti)

		n1, err = connToHost.Read(b)
		if err != nil {
			log.Println("<--> <--> ", n1, err.Error())
			time.Sleep(time.Millisecond * 100)
			connToHost.Close()
			continue
		}
		connToHosta = connToHost
		break
	}
	n2, _ = connToHosta.Read(b[n1:])
	n3, _ = connToHosta.Read(b[n1+n2:])
	n4, _ = connToHosta.Read(b[n1+n2+n3:])
	rall = n1 + n2 + n3 + n4
	log.Println("Reading from inside: ", rall, n1, n2, n3, n4)
	last := string(b[:rall])

	conLenIdx := strings.Index(last, "Content-Length: ")
	conLenSlice := make([]byte, 0, 5)
	for idx := conLenIdx + 16; ; idx++ {
		if last[idx] == '\r' {
			break
		}
		conLenSlice = append(conLenSlice, last[idx])
	}
	conLen, _ := strconv.Atoi(string(conLenSlice))

	already := 0
	for idx := len(last) - 1; ; idx-- {
		if last[idx] == '\n' {
			break
		}
		already++
	}

	leftLen := conLen - already
	log.Println("*************conLen: ", conLen)
	log.Println("*************leftLen: ", leftLen)
	for idx := 0; idx != leftLen; {
		zan, _ := connToHosta.Read(b[rall:])
		idx = idx + zan
		rall = rall + zan
	}

	buf300.Reset()
	buf300.Write(gen300())
	last = string(b[:rall])
	if strings.Index(last, `return": ["T", "h", "e", "y", " ", "f", "o", "u", "n", "d", " ", "m", "e", ".", "`) >= 0 {
		log.Println("this is not good i think")
		w, _ := conn.Write(headers)
		log.Println("header len:", w)
		w, _ = conn.Write(buf300.Bytes())
		log.Println("w len:", w)
	} else {
		flagGang := false
		flagKind := 0
		log.Print("this is ", os.Getenv("version"))
		xinb := b[:rall]
		for idx := conLenIdx; idx != rall-2; idx++ {
			if xinb[idx] == '\\' {
				flagGang = true
				if xinb[idx+1] == 'n' {
					xinb[idx] = '{'
					xinb[idx+1] = ']'
					flagKind = flagKind | 1
				} else if xinb[idx+1] == '"' {
					xinb[idx] = '['
					xinb[idx+1] = '}'
					flagKind = flagKind | 2
				}
			}
			if xinb[idx] == '\n' {
				if xinb[idx-1] != '\r' {
					flagKind = flagKind | 4
					xinb[idx] = '{'
					xinb[idx+1] = '{'
				}
			}
		}
		w, _ := conn.Write(xinb)
		log.Println("flag:", flagGang, flagKind)
		log.Println("should len:", rall, xinb[rall-1])
		log.Println("w len:", w)
	}
}
